package com.jpemartin.jgt;

import java.awt.*;
import javax.swing.JApplet;


/** This is a base class you can extend to create a Java game. 
 *  It allows your game to be run either as an applet or as an application.
 * 
 *  A good starting point for your code would be this:
 *  
 *  <PRE>
 *  public class MyGame extends BaseApp {

   public String getName() {
      // text in title bar when game is run as an application
      return "MyGame";
   }
   
   // width of the window 
   public int getWidth() { return ... }
   
   // height of the window
   public int getHeight() { return ... }

   // main game loop
   public void run() {
      createImageAndGraphics();
      while (_gameRunning) {
         drawWorld();
         showAndWait();
      }
   }
   
   // draw what the player sees
   public void drawWorld() {
      if (null==_dbImage) return;
      synchronized(_dbImage) {
         _dbg.setColor(Color.GREEN);
         _dbg.fillRect(0, 0, getWidth(), getHeight());
         
      }
   }
   
   // don't forget to replace "MyGame" here if you change the name of your class
   static public void main(String argv[]) { new MyGame().runAsApplet(argv); }
}
 *  </PRE>
 **/
public abstract class BaseApp extends JApplet implements Runnable, HasKeyboard, HasSize {

   /** window title **/
   protected String _name;
   /** main game thread **/
   protected Thread _mythread;
   /** always true for now **/
   protected boolean _directDraw = true;
   /** keeps track of keypresses **/
   protected KeyboardStatus _kb;
   

   /** the image that's shown on the screen. Use createImageAndGraphics() to make it. **/
   public Image _dbImage;
   /** a Graphics to draw in _dbImage. Use createImageAndGraphics() to make it, 
    * and make sure that every time you use this to draw, you do it within
    * synchronized (_dbImage) { ... }
    */
   public Graphics _dbg;
   
   
   /** set to true initially, and to false when the user
    *  clicks the "close" button. You should then release resources
    *  and let the run() method return so we can quit gracefully.
    */
   public boolean _gameRunning;

   // --- methods you must override ---
   
   // (this being abstract prevents programmers from never learning how to set
   // a title for their app)
   
   /** You must write this function, return a string that decsribes the game.
    *  It will be shown in the title bar when the game is ran as an application
    *  rather than an applet.
    */
   public abstract String getName();
   
   // (having these methods as abstract makes sure the user code won't compile if he doesn't override
   // them, preventing weirdness such as a 0-width game window).
   
   /** You must rewrite this function to return the height of the game window **/
   public abstract int getHeight();

   /** You must rewrite this function to return the width of the game window **/
   public abstract int getWidth();
   
   // -- methods you must call ---  

   
   /** The first time you call this, it creates _dbImage and _dbg. The image
    *  is set to the size given by getWidth() and getHeight().
    *  The second time you call this, it does nothing.
    */
   public void createImageAndGraphics() {

        // Initialize double buffer
        if (null == _dbImage) {
          _dbImage = createImage(getWidth(), getHeight());            
          _dbg = _dbImage.getGraphics();                   
        }
   }
      
   /** Call this function every time you're done drawing one frame.
    *  It will show the picture and wait a little bit.
    */
   public void showAndWait() {

      // Min priority, let applet thread draw the picture
      Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
      
      // Tell the app thread to show the new picture when it
      // gets the chance
      repaint();
      
      try {
        // sleep between every frame
        Thread.sleep(20);
        
      } catch (InterruptedException ex) {
        // that one frame will go faster, no big deal
      }
      
      // Max priority now, we must compute the next frame
      Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
   }

   /** This function helps you run the game as an application rather than an applet.
    * You must add the following function in your code:
    * static public void main(String argv[]) { new YourConstructor().runAsApplet(argv); }
    **/
   public void runAsApplet(String argv[]) {   
   
     Frame a = new Frame (getName());
     BaseApp ui = this;
     a.add("Center",ui);    
     ui.init();
     a.show();
     // we have to do this because setSize() sets the outside size (excluding title&such)
     // and the inset data is only available after we call show()
     Insets insets = a.getInsets();
     a.setSize(getWidth()+(insets.left+insets.right), getHeight()+(insets.top+insets.bottom));
     a.show();
     ui.start();
     // wait for the game thread to return, and then close the application
     /*
     boolean retry=true;
     while (retry) try {
       ui.join();
       retry=false;
     } catch (InterruptedException e) {
       // just wait some more
     }
     //System.out.println("Main thread returned");
     ui.destroy();
     a.dispose();
     */
   }

   // --- now functions that you don't need to extend or call explicitly ---
   
   /** called when the applet starts. Creates a thread for the game. **/
   public void start() {
      
      _kb = new KeyboardStatus();
      addKeyListener(_kb);
      
      synchronized(this){
        // make sure nothing bad happens if this is called twice
        // (can't trust the browser to only call us once)
        if (_gameRunning) return;
        
        _gameRunning=true;
        
        
        // start a new thread to run the game on
        _mythread = new Thread(this);
        _mythread.start();        
        
      }
      
    }
   

   /** called by AWT when the picture needs to be refreshed.
    * When _directDraw is enabled, we just send the double-buffer image dbImage to the screen. **/
   public void paint(Graphics g) {        
     if (!_directDraw) {
       // let Swing do its thing
       super.paint(g);
       return;
     }
     // mutex since we're accessing the shared dbImage
     if (null==_dbImage) return;
     synchronized (_dbImage) {
       g.drawImage(_dbImage, 0, 0, this);
     }
   }
   

   /** for HasKeyboard **/
   public com.jpemartin.jgt.KeyboardStatus getKeyboardStatus() {
      return _kb;
   }

   
}
